package me.kuye.jkuye.plugin.activerecord;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ModelBuilder {

	public static final <T> List<T> build(ResultSet result, Class<? extends Model> modelClass)
			throws SQLException, InstantiationException, IllegalAccessException {
		List<T> list = new ArrayList<T>();
		ResultSetMetaData resultSetMetaData = result.getMetaData();
		int columnCount = resultSetMetaData.getColumnCount();
		String[] labelNames = new String[columnCount + 1];
		int[] types = new int[columnCount + 1];
		buildLabelNamesAndTypes(resultSetMetaData, labelNames, types);
		while (result.next()) {
			Model<?> model = modelClass.newInstance();
			Map<String, Object> attrs = model.getAttrs();
			for (int i = 1; i < columnCount; i++) {
				Object value;
				if (types[i] < Types.BLOB) {
					value = result.getObject(i);
				} else if (types[i] == Types.CLOB) {
					value = handleClob(result.getClob(i));
				} else if (types[i] == Types.NCLOB) {
					value = handleClob(result.getNClob(i));
				} else if (types[i] == Types.BLOB) {
					value = handleBlob(result.getBlob(i));
				} else {
					value = result.getObject(i);
				}
			}
			list.add((T) model);
		}
		return list;

	}

	private static String handleClob(Clob clob) throws SQLException {
		if (clob == null)
			return null;

		Reader reader = null;
		try {
			reader = clob.getCharacterStream();
			char[] buffer = new char[(int) clob.length()];
			reader.read(buffer);
			return new String(buffer);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				reader.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}

	private static byte[] handleBlob(Blob blob) {
		if (blob == null) {
			return null;
		}
		InputStream input = null;
		try {
			input = blob.getBinaryStream();
			byte[] bytes = new byte[(int) blob.length()];
			input.read(bytes);
			input.close();
			return bytes;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			try {
				if (input != null) {
					input.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private static final void buildLabelNamesAndTypes(ResultSetMetaData resultSetMetaData, String[] labelNames,
			int[] types) throws SQLException {
		for (int i = 0; i < labelNames.length; i++) {
			labelNames[i] = resultSetMetaData.getColumnLabel(i);
			types[i] = resultSetMetaData.getColumnType(i);
		}
	}

}
